/**
 * 
 */
package com.wgo.bpot.common.transport.servicefacade.hessian;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;


import com.caucho.hessian.io.AbstractDeserializer;
import com.caucho.hessian.io.AbstractHessianInput;
import com.caucho.hessian.io.AbstractHessianOutput;
import com.caucho.hessian.io.AbstractSerializer;
import com.caucho.hessian.io.AbstractSerializerFactory;
import com.caucho.hessian.io.Deserializer;
import com.caucho.hessian.io.Serializer;
import com.wgo.bpot.common.transport.util.Assert;

/**
 * Due to hessian incompability.
 * 
 * @author petterei
 * @version $Id: MethodSerializerFactory.java,v 1.1 2006-01-23 09:27:22 petterei Exp $
 */
public class MethodSerializerFactory extends AbstractSerializerFactory {
	private static final Logger log = Logger.getLogger(MethodSerializerFactory.class.getName());
	
	private MethodSerializer methodSerializer = new MethodSerializer();
	
	private MethodDeserializer methodDeserializer = new MethodDeserializer();
	
	@Override
	public Serializer getSerializer(Class cl) {
		
		if (log.isLoggable(Level.FINER)) {
			log.log(Level.FINER, "Looking up serializer for: " + cl.getName());
		}
		if (Method.class.isAssignableFrom(cl)) {
			if (log.isLoggable(Level.FINER)) {
				log.log(Level.FINER, "Returning method serializer for class: " + cl.getName());
			}
			return methodSerializer;
		}
		
		return null;
	}
	
	@Override
	public Deserializer getDeserializer(Class cl) {
		
		if (log.isLoggable(Level.FINER)) {
			log.log(Level.FINER, "Looking up deserializer for: " + cl.getName());
		}
		if (Method.class.isAssignableFrom(cl)) {
			if (log.isLoggable(Level.FINER)) {
				log.log(Level.FINER, "Returning method deserializer for class: " + cl.getName());
			}
			return methodDeserializer;
		}
		return null;
	}
}
class MethodSerializer extends AbstractSerializer {
	private static final Logger log = Logger.getLogger(MethodSerializer.class.getName());
	
	@Override
	public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
		Method method = (Method)obj;
		Class declaringClass = method.getDeclaringClass();
		String name = method.getName();
		Class[] parameterTypes = method.getParameterTypes();
		log.fine("Writing method " + method.getName() + " declared by: " + declaringClass.getSimpleName());
		out.writeMapBegin(declaringClass.getName());
		out.writeString(declaringClass.getName());
		out.writeString(name);
		for (Class clazz : parameterTypes) {
			out.writeString(clazz.getName());
		}
		out.writeMapEnd();
		
	}
}
class MethodDeserializer extends AbstractDeserializer {
	
	private static final Logger log = Logger.getLogger(MethodDeserializer.class.getName());
	
	@Override
	public Object readMap(AbstractHessianInput in) throws IOException {
		//in.readMapStart();
		String declaringClassString = in.readString();
		String methodName = in.readString();
		
		List<String> parameterTypesString = new ArrayList<String>();
		
		while (!in.isEnd()) {
			parameterTypesString.add(in.readString());
		}
		in.readMapEnd();
		
		Assert.assertNotNull(declaringClassString, "MethodDeserializer: Expected declaring class.");
		Assert.assertNotNull(methodName, "MethodDeserializer: Expected method name.");
		
		log.fine("Reading method " + methodName + " declared by " + declaringClassString);
		
		Class declaringClass = null;
		declaringClass = internalRetrieveClass(declaringClassString);
		
		Class[] parameterTypes = new Class[parameterTypesString.size()];
		int i = 0;
		for (String parameterType : parameterTypesString) {
			parameterTypes[i] = internalRetrieveClass(parameterType);
			i++;
		}
		Method method = null;
		try {
			method = declaringClass.getMethod(methodName, parameterTypes);
		} catch (Exception e) {
			throw new IOException("MethodDeserializer: Expected method (" + methodName + " declared by "+declaringClass.getSimpleName()+") was not found. Exception: " + e.getClass().getSimpleName()+ " Message: " +e.getMessage());
		}
		return method;
	}
	
	private static Class internalRetrieveClass(String className) throws IOException {
		Class clazz = null;
		try {
			clazz = Class.forName(className);
		} catch (ClassNotFoundException e) {
			throw new IOException("MethodDeserializer: Expected class (" + className + ") was not found. Exception message: " + e.getMessage());
		}
		return clazz;
	}
}